summaryrefslogtreecommitdiff
path: root/src2/pages/shop/product/[slug].js
diff options
context:
space:
mode:
Diffstat (limited to 'src2/pages/shop/product/[slug].js')
-rw-r--r--src2/pages/shop/product/[slug].js305
1 files changed, 305 insertions, 0 deletions
diff --git a/src2/pages/shop/product/[slug].js b/src2/pages/shop/product/[slug].js
new file mode 100644
index 00000000..61692c1c
--- /dev/null
+++ b/src2/pages/shop/product/[slug].js
@@ -0,0 +1,305 @@
+import Link from "@/components/elements/Link"
+import { useRouter } from "next/router"
+import { useEffect, useState } from "react"
+import Header from "@/components/layouts/Header"
+import apiOdoo from "@/core/utils/apiOdoo"
+import { createSlug, getIdFromSlug } from "@/core/utils/slug"
+import currencyFormat from "@/core/utils/currencyFormat"
+import Layout from "@/components/layouts/Layout"
+import { createOrUpdateItemCart } from "@/core/utils/cart"
+import toast from "react-hot-toast"
+import Footer from "@/components/layouts/Footer"
+import Image from "@/components/elements/Image"
+import LineDivider from "@/components/elements/LineDivider"
+import { HeartIcon as HeartIconSolid } from "@heroicons/react/24/solid"
+import { useAuth } from "@/core/utils/auth"
+import { HeartIcon } from "@heroicons/react/24/outline"
+import LazyLoad from "react-lazy-load"
+import ProductSimilar from "@/components/products/ProductSimilar"
+
+export async function getServerSideProps( context ) {
+ const { slug } = context.query
+ let product = await apiOdoo('GET', '/api/v1/product/' + getIdFromSlug(slug))
+ if (product?.length == 1) {
+ product = product[0]
+ product.description = product.description.replaceAll('<p>', '||p||')
+ product.description = product.description.replaceAll('</p>', '||/p||')
+ product.description = product.description.replace(/(<([^>]+)>)/gi, ' ')
+ product.description = product.description.replaceAll('||p||', '<p>')
+ product.description = product.description.replaceAll('||/p||', '</p>')
+ product.description = product.description.trim()
+ }
+ return { props: { product } }
+}
+
+export default function ProductDetail({ product }) {
+ const [ auth ] = useAuth()
+ const router = useRouter()
+ const { slug } = router.query
+ const [selectedVariant, setSelectedVariant] = useState("")
+ const [quantity, setQuantity] = useState("1")
+ const [activeVariant, setActiveVariant] = useState({
+ id: product.id,
+ code: product.code,
+ price: product.lowest_price,
+ stock: product.stock_total,
+ weight: product.weight,
+ attributes: '',
+ })
+
+ const [ isAddedToWishlist, setAddedToWishlist ] = useState(false)
+ const [ activeTab, setActiveTab ] = useState('specification')
+
+ const addOrDeleteWishlist = async () => {
+ if (auth) {
+ await apiOdoo('POST', `/api/v1/user/${auth.id}/wishlist/create-or-delete`, {
+ product_id: product.id
+ })
+ if (isAddedToWishlist) {
+ toast.success('Berhasil menghapus dari wishlist')
+ } else {
+ toast.success('Berhasil menambahkan ke wishlist')
+ }
+ setAddedToWishlist(!isAddedToWishlist)
+ } else {
+ toast.error('Login terlebih dahulu untuk melanjutkan')
+ router.push('/login')
+ }
+ }
+
+ useEffect(() => {
+ if (auth) {
+ const checkWishlist = async () => {
+ const wishlist = await apiOdoo('GET', `/api/v1/user/${auth.id}/wishlist?product_id=${product.id}`)
+ setAddedToWishlist(wishlist.product_total > 0 ? true : false)
+ }
+ checkWishlist()
+ }
+ }, [ auth, product ])
+
+ useEffect(() => {
+ if (product.variants.length == 1) {
+ setSelectedVariant(product.variants[0].id)
+ }
+ }, [ product ])
+
+ useEffect(() => {
+ if (selectedVariant != '') {
+ let newActiveVariant = product.variants.filter((variant) => {
+ return variant.id == selectedVariant
+ })
+
+ if (newActiveVariant.length == 1) {
+ newActiveVariant = newActiveVariant[0]
+ setActiveVariant({
+ id: newActiveVariant.id,
+ code: newActiveVariant.code,
+ price: newActiveVariant.price,
+ stock: newActiveVariant.stock,
+ weight: newActiveVariant.weight,
+ attributes: newActiveVariant.attributes.join(', '),
+ })
+ }
+ }
+ }, [selectedVariant, product])
+
+ const onchangeVariant = (e) => {
+ setSelectedVariant(e.target.value)
+ }
+
+ const onChangeQuantity = (e) => {
+ let inputValue = e.target.value
+ inputValue = parseInt(inputValue)
+ inputValue = Math.floor(inputValue)
+ setQuantity(inputValue)
+ }
+
+ const addItemToCart = () => {
+ if (product.variant_total > 1 && !selectedVariant) {
+ toast.error('Pilih varian terlebih dahulu untuk menambahkan ke keranjang', { duration: 2000 })
+ return false
+ }
+
+ if (quantity > 0) {
+ toast.success('Berhasil menambahkan ke keranjang', { duration: 1500 })
+ createOrUpdateItemCart(activeVariant.id, parseInt(quantity))
+ } else {
+ toast.error('Jumlah barang yang ditambahkan minimal 1 pcs', { duration: 2000 })
+ }
+
+ return true
+ }
+
+ const checkoutProduct = () => {
+ if (!auth) {
+ toast.error('Login terlebih dahulu untuk melanjutkan', { duration: 2000 })
+ router.push('/login')
+ return
+ }
+ if (product.variant_total > 1 && !selectedVariant) {
+ toast.error('Pilih varian terlebih dahulu untuk melanjutkan pembelian', { duration: 2000 })
+ return
+ }
+ if (quantity < 0) {
+ toast.error('Jumlah barang yang ditambahkan minimal 1 pcs', { duration: 2000 })
+ return
+ }
+ router.push(`/shop/checkout?product_id=${activeVariant.id}&qty=${quantity}`)
+ }
+
+ const TabButton = ({ children, name }) => (
+ <button
+ type="button"
+ className={`font-medium pb-1 ${activeTab == name ? 'text-red_r-11 border-b border-red_r-10' : 'text-gray_r-11'}`}
+ onClick={() => setActiveTab(name)}
+ >
+ { children }
+ </button>
+ )
+
+ return (
+ <>
+ <Header title={`${product.name} - Indoteknik`}/>
+ <Layout>
+ <Image
+ src={product.image}
+ alt={product.name}
+ className="border-b border-gray_r-6 w-full h-[300px] object-contain object-center bg-white"
+ />
+
+ <div className="p-4">
+ <div className="flex justify-between gap-x-3">
+ <div>
+ <Link href={'/shop/brands/' + createSlug(product.manufacture.name, product.manufacture.id)}>
+ {product.manufacture.name ?? '-'}
+ </Link>
+ <h1 className="h2 mt-2 mb-3">{product.name}{activeVariant.attributes ? ' - ' + activeVariant.attributes : ''}</h1>
+ </div>
+ <button className="h-fit" onClick={addOrDeleteWishlist}>
+ { isAddedToWishlist && (
+ <HeartIconSolid className="w-6 text-red_r-10" />
+ ) }
+ { !isAddedToWishlist && (
+ <HeartIcon className="w-6" />
+ ) }
+ </button>
+ </div>
+
+ {product.variant_total > 1 && !selectedVariant && product.lowest_price.price > 0 ? (
+ <p className="text-caption-2 text-gray-800 mb-1">Harga mulai dari:</p>
+ ) : ''}
+
+ {product.lowest_price.discount_percentage > 0 ? (
+ <div className="flex gap-x-1 items-center mb-1">
+ <p className="text-caption-2 text-gray_r-11 line-through">{currencyFormat(activeVariant.price.price)}</p>
+ <span className="badge-solid-red">{activeVariant.price.discount_percentage}%</span>
+ </div>
+ ) : ''}
+
+ {product.lowest_price.price > 0 ? (
+ <p className="text-body-lg font-semibold">{currencyFormat(activeVariant.price.price_discount)}</p>
+ ) : (
+ <p className="text-gray_r-11">Dapatkan harga terbaik, <a href="">hubungi kami.</a></p>
+ )}
+ </div>
+
+ <LineDivider />
+
+ <div className="p-4">
+ <div className="">
+ <label className="form-label mb-2">Pilih: <span className="text-gray_r-11 font-normal">{product.variant_total} Varian</span></label>
+ <select name="variant" className="form-input" value={selectedVariant} onChange={onchangeVariant} >
+ <option value="" disabled={selectedVariant != "" ? true : false}>Pilih Varian...</option>
+ {product.variants.length > 1 ? (
+ product.variants.map((variant) => {
+ return (
+ <option key={variant.id} value={variant.id}>{variant.attributes.join(', ')}</option>
+ )
+ })
+ ) : (
+ <option key={product.variants[0].id} value={product.variants[0].id}>{product.variants[0].name}</option>
+ )}
+ </select>
+ </div>
+
+ <label htmlFor="quantity" className="form-label mb-1 mt-3">Jumlah</label>
+ <div className="flex gap-x-2 mt-2">
+ <input type="number" name="quantity" id="quantity" className="form-input h-full w-5/12 text-center" value={quantity} onChange={onChangeQuantity} />
+
+ <button
+ className="btn-yellow w-full"
+ onClick={addItemToCart}
+ disabled={(product.lowest_price.price == 0 ? true : false)}
+ >
+ Keranjang
+ </button>
+ <button
+ onClick={checkoutProduct}
+ className="btn-solid-red w-full"
+ >
+ Beli
+ </button>
+ </div>
+ </div>
+
+ <LineDivider />
+
+ <div className="p-4">
+ <h2 className="font-bold mb-4">Informasi Produk</h2>
+ <div className="flex gap-x-3 mb-4">
+ <TabButton name="specification">Spesifikasi</TabButton>
+ <TabButton name="description">Deskripsi</TabButton>
+ <TabButton name="information">Info Penting</TabButton>
+ </div>
+
+ <div className={`border border-gray_r-6 rounded divide-y ${activeTab == 'specification' ? 'block' : 'hidden'}`}>
+ <ProductSpecification label="Jumlah Varian">
+ <p className="text-gray-800">{product.variant_total} Varian</p>
+ </ProductSpecification>
+ <ProductSpecification label="Nomor SKU">
+ <p className="text-gray-800" id="sku_number">SKU-{activeVariant.id}</p>
+ </ProductSpecification>
+ <ProductSpecification label="Part Number">
+ <p className="text-gray-800" id="part_number">{activeVariant.code}</p>
+ </ProductSpecification>
+ <ProductSpecification label="Stok">
+ <div className="flex gap-x-2" id="stock">
+ {activeVariant.stock > 0 ? (activeVariant.stock > 5 && (
+ <>
+ <div className="badge-solid-red">Ready Stock</div>
+ <div className="badge-gray">{activeVariant.stock > 5 ? '> 5' : '< 5'}</div>
+ </>
+ )) : '0'}
+ </div>
+ </ProductSpecification>
+ <ProductSpecification label="Part Number">
+ <p className="text-gray-800" id="weight">{activeVariant.weight > 0 ? activeVariant.weight : '1'} KG</p>
+ </ProductSpecification>
+ </div>
+
+ <div
+ className={`text-gray-800 leading-7 ${activeTab == 'description' ? 'block' : 'hidden'}`}
+ dangerouslySetInnerHTML={{__html: (product.description != '' ? product.description : 'Belum ada deskripsi produk.')}}
+ ></div>
+ </div>
+
+ <LineDivider />
+
+ <LazyLoad>
+ <ProductSimilar productId={getIdFromSlug(slug || '')} />
+ </LazyLoad>
+
+ <Footer />
+ </Layout>
+ </>
+ )
+}
+
+const ProductSpecification = ({ children, ...props }) => {
+ return (
+ <div className="flex p-3 justify-between items-center gap-x-1">
+ <h3 className="text-gray-900">{ props.label }</h3>
+ { children }
+ </div>
+ )
+} \ No newline at end of file